﻿// ===============================================================================
// XCI.NET 西安交通信息WinForm开发框架 2018 版权所有
// ===============================================================================
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Threading;
using XCI.Core;
using XCI.Helper;
using XCI.Sys.Helper;
using XCI.Sys.Model;
using XCI.Windows.Extensions;

namespace XCI.Sys
{
    /// <summary>
    /// 软件更新组件
    /// </summary>
    public class Upd
    {
        private readonly ManualResetEvent _downloadResetEvent = new ManualResetEvent(false);
        private string _downloadTempFile;//下载的临时文件
        private UpdatePackageInfo _currentUpdatePackageInfo;//当前更新信息

        #region 事件

        /// <summary>
        /// 消息输出
        /// </summary>
        public Action<string> MessageCallback { get; set; }

        /// <summary>
        /// 下载更新包进度输出
        /// </summary>
        public Action<int> DownloadProgressCallback { get; set; }

        /// <summary>
        /// 下载更新包完成回调
        /// </summary>
        public Action DownloadCompleteCallback { get; set; }

        /// <summary>
        /// 上传更新包进度输出
        /// </summary>
        public Action<int> UploadProgressCallback { get; set; }

        /// <summary>
        /// 上传更新完成回调
        /// </summary>
        public Action UploadCompleteCallback { get; set; }

        /// <summary>
        /// 消息回调
        /// </summary>
        /// <param name="msg">消息</param>
        private void OnMessage(string msg)
        {
            MessageCallback?.Invoke(msg);
        }

        /// <summary>
        /// 下载更新包进度输出
        /// </summary>
        /// <param name="progress">进度百分比</param>
        private void OnDownloadProgress(int progress)
        {
            DownloadProgressCallback?.Invoke(progress);
        }

        /// <summary>
        /// 下载更新包完成回调
        /// </summary>
        private void OnDownloadComplete()
        {
            DownloadCompleteCallback?.Invoke();
        }

        /// <summary>
        /// 上传更新包进度输出
        /// </summary>
        /// <param name="progress">进度百分比</param>
        private void OnUploadProgress(int progress)
        {
            UploadProgressCallback?.Invoke(progress);
        }

        /// <summary>
        /// 上传更新完成回调
        /// </summary>
        private void OnUploadComplete()
        {
            UploadCompleteCallback?.Invoke();
        }

        #endregion

        ///// <summary>
        ///// 检测更新,如果发现更新返回更新内容,否则返回null
        ///// </summary>
        //public UpdInfo CheckUpdate()
        //{
        //    if (String.IsNullOrEmpty(setting.AppId))
        //    {
        //        OnMessage("请指定应用程序标识");
        //    }
        //    using (var client = CreateClient())
        //    {
        //        var data = new NameValueCollection();
        //        data.Add("App", setting.AppId);
        //        data.Add("Action", "CheckUpdate");
        //        var bytes = client.UploadValues(_url, "post", data);
        //        if (bytes.Length > 0)
        //        {
        //            string json = Encoding.UTF8.GetString(bytes);
        //            var lastInfo = JsonHelper.Deserialize<UpdInfo>(json);
        //            if (lastInfo.Version > setting.AppLocalVersion)
        //            {
        //                return lastInfo;
        //            }
        //        }
        //        return null;
        //    }
        //}

        /// <summary>
        /// 开始更新
        /// </summary>
        public void StartUpdate()
        {
            if (String.IsNullOrEmpty(SysRuntime.Setting.Id)) return;
            if (!SysRuntime.Setting.EnableUpdate) return;
            var client = new WebClientx();
            client.Timeout = SysRuntime.Setting.ApiTimeout;
            client.DownloadProgressChanged += client_DownloadProgressChanged;
            client.DownloadFileCompleted += client_DownloadFileCompleted;
            string msg;
            try
            {
                OnMessage("正在查找可用更新...");
                var preList = GetPreUpdateList();
                if (preList != null && preList.Count > 0)
                {
                    foreach (var item in preList)
                    {
                        _downloadResetEvent.Reset();
                        _currentUpdatePackageInfo = item;
                        _downloadTempFile = Path.Combine(SystemDirectory.TempDirectory, StringHelper.Guid() + ".zip");
                        IOHelper.CreateDirectoryByPath(_downloadTempFile);
                        client.DownloadFileAsync(new Uri(_currentUpdatePackageInfo.Url), _downloadTempFile);
                        _downloadResetEvent.WaitOne();
                    }
                    OnMessage("正在重启应用程序...");
                    AppHelper.RestartApplication();
                    //Process.Start(Application.ExecutablePath, String.Format("-restart {0} -upd", Process.GetCurrentProcess().Id));
                    //Environment.Exit(0);
                }
                //else
                //{
                //    OnMessage("没有可用更新");
                //}
            }
            catch (WebException ex)
            {
                msg = ex.Message;
                if (ex.Status == WebExceptionStatus.ConnectFailure)
                {
                    msg = "无法连接到更新服务器";
                }
                else if (ex.Status == WebExceptionStatus.Timeout)
                {
                    msg = "连接到更新服务器超时";
                }
                OnMessage(msg);
                Thread.Sleep(2000);
            }
            catch (Exception ex)
            {
                msg = ex.Message;
                OnMessage(msg);
                Thread.Sleep(2000);
            }
            finally
            {
                client.Dispose();
                _downloadResetEvent.Dispose();
            }
        }

        ///// <summary>
        ///// 上传更新包
        ///// </summary>
        ///// <param name="uploadFilePath">更新包路径</param>
        ///// <returns>上传成功返回True</returns>
        //public BoolMessage<FileModel> UploadPack(string uploadFilePath)
        //{
        //    var url = "/api/sys/upgrade/add";
        //    return SysRuntime.Api.UploadFile<FileModel>(url, "pack.zip", uploadFilePath).EnsureSuccess();
        //}

        /// <summary>
        /// 获取更新包列表
        /// </summary>
        public List<UpdatePackageInfo> GetUpdateList()
        {
            var url = "/api/sys/upgrade/list";
            var result = SysRuntime.Api.Post<List<UpdatePackageInfo>>(url);
            if (result.Success)
            {
                return result.Data;
            }
            return new List<UpdatePackageInfo>();
        }

        public static string GetLocalVersion()
        {
            var path = IOHelper.GetOutFilePath("version.ini");
            var version = IniFileHelper.Read(path, "upgrade", "version");
            if (string.IsNullOrEmpty(version))
            {
                version = "0";
            }
            return version;
        }

        public static void SetLocalVersion(string version)
        {
            var path = IOHelper.GetOutFilePath("version.ini");
            IniFileHelper.Write(path, "upgrade", "version", version);
        }

        #region 私有函数

        //private WebClientx CreateClient()
        //{
        //    var client = new WebClientx();
        //    client.Timeout = AppRuntime.AppSetting.SystemTimeout;
        //    return client;
        //}

        /// <summary>
        /// 获取更新包列表
        /// </summary>
        private List<UpdatePackageInfo> GetPreUpdateList()
        {
            var url = "/api/sys/upgrade/update";
            var data = Map.New.Add("version", GetLocalVersion());
            var result = SysRuntime.Api.Post<List<UpdatePackageInfo>>(url, data);
            if (result.Success)
            {
                return result.Data;
            }
            return new List<UpdatePackageInfo>();
        }

        private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            OnDownloadProgress(e.ProgressPercentage);
            OnMessage($"更新版本{_currentUpdatePackageInfo.Version}:正在下载文件 {e.ProgressPercentage}%");
        }

        private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            OnDownloadComplete();
            UpdateApp();
        }

        /// <summary>
        /// 替换文件
        /// </summary>
        /// <param name="backupDir">备份目录</param>
        /// <param name="targetDir">目标目录</param>
        /// <param name="currentDir">当前目录</param>
        private void ReplaceFile(string backupDir, string targetDir, string currentDir)
        {
            if (!Directory.Exists(backupDir))
            {
                Directory.CreateDirectory(backupDir);
            }

            foreach (string file in Directory.GetFiles(targetDir))
            {
                string fileName = Path.GetFileName(file);
                if (string.IsNullOrEmpty(fileName)) continue;
                //if (fileName.Equals("LocalVersion.json", StringComparison.OrdinalIgnoreCase)) continue;//本地版本配置文件不覆盖
                if (!Directory.Exists(currentDir))
                {
                    Directory.CreateDirectory(currentDir);
                }
                string sourceFilePath = Path.Combine(currentDir, fileName);
                if (File.Exists(sourceFilePath))
                {
                    File.Move(sourceFilePath, Path.Combine(backupDir, fileName));
                }
                File.Copy(file, sourceFilePath);
            }

            foreach (string dir in Directory.GetDirectories(targetDir))
            {
                string dirName = Path.GetFileName(dir);
                if (string.IsNullOrEmpty(dirName)) continue;
                string subDir = Path.Combine(backupDir, dirName);
                ReplaceFile(subDir, dir, Path.Combine(currentDir, dirName));
            }
        }

        /// <summary>
        /// 更新应用程序
        /// </summary>
        private void UpdateApp()
        {
            OnMessage($"更新版本{_currentUpdatePackageInfo.Version}:正在解压更新文件...");
            string extractDir = Path.Combine(SystemDirectory.TempDirectory, StringHelper.Guid());
            IOHelper.CreateDirectoryByPath(extractDir);
            ZipHelper.UnCompressDirectory(_downloadTempFile, extractDir);

            OnMessage($"更新版本{_currentUpdatePackageInfo.Version}:正在应用更新...");
            string versionBackupDir = Path.Combine(SystemDirectory.BackupDirectory, "Version" + _currentUpdatePackageInfo.Version);
            if (Directory.Exists(versionBackupDir))
            {
                Directory.Delete(versionBackupDir, true);
            }
            ReplaceFile(versionBackupDir, extractDir, IOHelper.RootDirectory);
            OnMessage($"更新版本{_currentUpdatePackageInfo.Version}:正在删除临时文件...");
            File.Delete(_downloadTempFile);
            Directory.Delete(extractDir, true);
            OnMessage($"更新版本{_currentUpdatePackageInfo.Version}:正在更新配置文件...");
            //localVersion.Data.Version = _currentUpdInfo.Version;
            //localVersion.Save();
            SetLocalVersion(_currentUpdatePackageInfo.Version.ToString());
            //SysRuntime.UserSetting.AppLocalVersion = _currentUpdatePackageInfo.Version;
            //SysRuntime.App.SaveUserSetting();
            _downloadResetEvent.Set();
        }

        #endregion
    }

    /// <summary>
    /// 系统更新包信息
    /// </summary>
    public class UpdatePackageInfo
    {
        /// <summary>
        /// 版本号
        /// </summary>		
        public int Version { get; set; }

        /// <summary>
        /// 创建日期
        /// </summary>
        public DateTime CreateTime { get; set; }

        /// <summary>
        /// 文件名称
        /// </summary>		
        public string Name { get; set; }

        /// <summary>
        /// 下载路径
        /// </summary>
        public string Url { get; set; }
    }
}